from openai import OpenAI
from dotenv import load_dotenv
import os
from abc import ABC, abstractmethod


class AIServiceBase(ABC):
    """
    AI服务基类，定义通用接口方法
    1. 封装不同AI平台API的通用调用逻辑
    2. 提供模板方法供子类实现特定平台逻辑
    3. 实现流式和非流式响应处理
    """

    # 当一个类需要作为其他类的模板,并且:
    # 1. 有方法必须要求子类实现(使用@abstractmethod装饰器)
    # 2. 不希望这个基类被直接实例化
    # 这时就需要继承ABC(Abstract Base Class)
    #
    # 如果一个类:
    # 1. 不需要强制子类实现某些方法
    # 2. 允许直接实例化使用
    # 这时就不需要继承ABC

    def __init__(self, api_key):
        """初始化AI服务实例
        Args:
            api_key: 平台API密钥
        """
        self.api_key = api_key
        self.client = self.generate_client(api_key, self.get_base_url())

    @abstractmethod
    def get_base_url(self):
        """抽象方法：获取平台API基础URL
        子类必须实现此方法以返回特定平台的API地址
        """
        pass

    @classmethod
    def generate_client(cls, api_key, base_url):
        """生成OpenAI客户端实例
        Args:
            api_key: 平台API密钥
            base_url: 平台API基础URL
        Returns:
            OpenAI客户端实例
        """
        return OpenAI(api_key=api_key, base_url=base_url)

    def ai_chat(self, prompt, **kwargs):
        """执行AI聊天对话
        Args:
            prompt: 用户输入的提示词
            **kwargs: 可选参数
                - model: 使用的AI模型名称(默认值由子类提供，必须提供)
                - temperature: 控制生成随机性的参数(默认1)
                - max_tokens: 最大返回token数量(默认4096)
                - stream: 是否使用流式响应(默认True)
        Returns:
            完整的对话响应内容
        """
        # 设置默认参数
        model = kwargs.get("model")
        if not model:
            print("参数model必须提供！")
            return None
        temperature = kwargs.get("temperature", 1.0)
        max_tokens = kwargs.get("max_tokens", 4096)
        stream = kwargs.get("stream", True)

        # 确保客户端已初始化
        if not hasattr(self, "client"):
            self.client = self.generate_client(self.api_key, self.get_base_url())

        # 创建API请求
        response = self.client.chat.completions.create(
            model=model,
            messages=[
                {"role": "user", "content": prompt},
            ],
            stream=stream,
            max_tokens=max_tokens,
            temperature=temperature,
        )

        # 处理响应
        if stream:
            contents = ""
            for chunk in response:
                chunk_content = chunk.choices[0].delta.content
                print(chunk_content, end="", flush=True)  # 实时打印流式响应
                contents += chunk_content
        else:
            contents = response.choices[0].message.content  # 非流式响应直接获取完整内容
            print(contents)

        return contents


class DeepseekService(AIServiceBase):
    """Deepseek平台服务实现
    1. 使用Deepseek官方API
    2. 默认模型为deepseek-chat
    """

    def get_base_url(self):
        """返回Deepseek平台API基础URL"""
        return "https://api.deepseek.com"

    def ai_chat(self, prompt, model="deepseek-chat", **kwargs):
        """执行Deepseek平台AI聊天
        重写父类方法，提供Deepseek平台默认参数
        """
        return super().ai_chat(prompt, model=model, **kwargs)


class VolcengineService(AIServiceBase):
    """火山引擎平台服务实现
    1. 使用火山引擎AI平台API
    2. 默认模型为deepseek-v3-250324
    """

    def get_base_url(self):
        """返回火山引擎平台API基础URL"""
        return "https://ark.cn-beijing.volces.com/api/v3"

    def ai_chat(self, prompt, model="deepseek-v3-250324", **kwargs):
        """执行火山引擎平台AI聊天
        重写父类方法，提供火山引擎平台默认参数
        """
        return super().ai_chat(prompt, model=model, **kwargs)


class SiliconflowService(AIServiceBase):
    """硅基流动平台服务实现
    1. 使用硅基流动AI平台API
    2. 默认模型为deepseek-ai/DeepSeek-V3
    """

    def get_base_url(self):
        """返回硅基流动平台API基础URL"""
        return "https://api.siliconflow.cn/v1"

    def ai_chat(self, prompt, model="deepseek-ai/DeepSeek-V3", **kwargs):
        """执行硅基流动平台AI聊天
        重写父类方法，提供硅基流动平台默认参数
        """
        return super().ai_chat(prompt, model=model, **kwargs)


def main():
    """
    演示多平台服务调用
    1. 从.env文件加载各平台API密钥
    2. 初始化三个平台服务实例
    3. 使用相同提示词测试各平台响应
    """
    load_dotenv()
    DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
    VOLCENGINE_API_KEY = os.getenv("VOLCENGINE_API_KEY")
    SILICONFLOW_API_KEY = os.getenv("SILICONFLOW_API_KEY")

    services = [
        DeepseekService(DEEPSEEK_API_KEY),
        VolcengineService(VOLCENGINE_API_KEY),
        SiliconflowService(SILICONFLOW_API_KEY),
    ]

    prompt = "你好，你是什么模型？"

    for service in services:
        print(f"\n使用{service.__class__.__name__}平台：")
        service.ai_chat(prompt)
        print("\n", "-" * 50)


if __name__ == "__main__":
    main()

"""
评估哪些类的方法可以改造为类方法或静态方法？
1. 将 `generate_client` 方法改造为类方法（使用 `@classmethod` 装饰器）。这个改动是合理的，因为：
   - 该方法不依赖于实例的状态，只需要 API 密钥和基础 URL 就能创建客户端
   - 将其设计为类方法可以提高代码的复用性，允许在不创建实例的情况下生成客户端
   - 方法的职责更加清晰，专注于客户端的创建

2. 相应的代码修改包括：
   - 在 `__init__` 方法中直接初始化客户端实例
   - 更新了 `generate_client` 方法的参数和文档
   - 修改了 `ai_chat` 方法中客户端初始化的逻辑

3. 其他方法分析：
   - `get_base_url` 必须保持为实例方法，因为它是抽象方法，需要子类实现具体的 URL
   - `ai_chat` 需要保持为实例方法，因为它依赖于实例的状态（client 实例）
"""
